#include "TagsManager.h"

#ifdef WIN32
const string TAGS_FOLDER = "\\tags\\";
#else
const string TAGS_FOLDER = "tags/";
#endif
      
TagsManager::TagsManager()
{
         this->directorioBase = "";
};

TagsManager::~TagsManager()
{
};

string TagsManager::IdToString(TCId id)
{
        char cAux[10];
        sprintf(cAux, "%u", id);
        string sResult(cAux);
        return sResult;
}

bool TagsManager::InicializarManager(string DirectorioInicial)
{
        this->directorioBase = DirectorioInicial + TAGS_FOLDER;
        return true;
}

bool TagsManager::AgregarCategoria(TCId idCategoria)
{
        Archivo archivoCategoria;
        archivoCategoria.setNombreArchivo(this->directorioBase +
                IdToString(idCategoria) + ".idx");

        if (!archivoCategoria.abrirArchivo(maActualizacion))
        {
                //no lo pude abrir para lectura                
                if (!archivoCategoria.abrirArchivo(maEscrituraCreacion))
                {
                        //no lo pude abrir para leer ni crearlo.=> error
                        return false;
                }
        }

        archivoCategoria.cerrarArchivo();
        return true;
}

bool TagsManager::AgregarNoticia(TCId idCategoria, TCId idFeed,
        TCPosicionEnLista posicion, TCTimeStamp fechaNoticia)
{
        Archivo archivoCategoria;
        archivoCategoria.setNombreArchivo(this->directorioBase +
                IdToString(idCategoria) + ".idx");

        if (archivoCategoria.abrirArchivo(maActualizacion))
        {
                //el archivo de categorias esta abierto...
                if (archivoCategoria.posicionarAlFinal())
                {
                        RegistroFijoTagIndex registroTagIndex;

                        registroTagIndex.setEstado(true);
                        registroTagIndex.setIdFeed(idFeed);
                        registroTagIndex.setPosicionEnIndice(posicion);
                        registroTagIndex.setFechaNoticia(fechaNoticia);

                        if (archivoCategoria.escribirRegistro((Registro*)&registroTagIndex))
                        {
                                archivoCategoria.cerrarArchivo();
                                return true;
                        }
                        else
                        {
                                archivoCategoria.cerrarArchivo();
                                return false;
                        }
                }
                else
                {
                        archivoCategoria.cerrarArchivo();
                        return false;
                }
        }
        else
        {
                return false;
        }
}

ConjuntoTagItemConsulta TagsManager::ConsultaPorCategoria(TCId idCategoria)
{
        ConjuntoTagItemConsulta resultado;
        TagItemConsulta* itemTagConsulta;
        RegistroFijoTagIndex registroData;        

        Archivo archivoCategoria;
        archivoCategoria.setNombreArchivo(this->directorioBase +
                IdToString(idCategoria) + ".idx");

        if (archivoCategoria.abrirArchivo(maActualizacion))
        {
                archivoCategoria.posicionarEnArchivo(0);
                
                while (!archivoCategoria.getEsFinDeArchivo())
                {
                        if (archivoCategoria.leerRegistro((Registro*)&registroData))
                        {
                                if (registroData.getEstado())
                                {
                                        itemTagConsulta = new TagItemConsulta();
                                        itemTagConsulta->setIdFeed(registroData.getIdFeed());
                                        itemTagConsulta->setPosicion(registroData.getPosicionEnIndice());
                                        itemTagConsulta->setFechaNoticia(registroData.getFechaNoticia());
                                        resultado.insert(*itemTagConsulta);
                                        //resultado.push_back(*itemTagConsulta);
                                        delete itemTagConsulta;
                                }
                        }
                }
                archivoCategoria.cerrarArchivo();
        }        

        return resultado;        
}

ConjuntoTagItemConsulta TagsManager::Consulta(set<int> feeds, ConjuntoIdCategoria categoriasAIncluir,
        ConjuntoIdCategoria categoriasANoIncluir, TCTimeStamp since , TCTimeStamp until, NewsManager* newsManager)
{
        ConjuntoTagItemConsulta resultadoInclusion;
        ConjuntoTagItemConsulta itemsCategoria;
        ConjuntoTagItemConsulta::iterator iteradorNoticias;
        ConjuntoPosiciones posicionIndiceNoticia;
        ConjuntoIdCategoria::iterator iteradorCategoriaAIncluir;
        //ConjuntoIdCategoria::iterator itParaPositivo;
        ConjuntoIdCategoria::iterator itParaNegativo;
        ListaConsultaNoticias elementoNoticia;
        ListaConsultaNoticias::iterator itElementoNoticia;
        ListaCategorias* tagsPos;
        ListaCategorias* tagsNeg;
        ListaCategorias::iterator iterTagsPos;
        ListaCategorias::iterator iterTagsNeg;
        bool bExclude;
        bool bInclude;
		unsigned int countTemp;
        
        if (categoriasAIncluir.size() > 0)
        {
                iteradorCategoriaAIncluir = categoriasAIncluir.begin();
                itemsCategoria = this->ConsultaPorCategoria(*iteradorCategoriaAIncluir);

                //si da 0, ya se que no voy a devolver nada
                if (itemsCategoria.size() > 0)
                {
                        //para cada una de estas, me fijo sus lista de positivos.
                        for(iteradorNoticias = itemsCategoria.begin();
                            iteradorNoticias != itemsCategoria.end();
                            iteradorNoticias++)
                        {
                                //consulto solo esta noticia en newsmanager...
                                posicionIndiceNoticia.clear();
                                posicionIndiceNoticia.insert((*iteradorNoticias).getPosicion());

                                elementoNoticia.clear();
                                elementoNoticia = newsManager->consultarNews(posicionIndiceNoticia, (*iteradorNoticias).getIdFeed());

                                if (elementoNoticia.size() == 1)
                                {
                                        //me asegure que me trajo solo una noticia.
                                        //obtengo la lista de positivos de la noticia
                                        itElementoNoticia = elementoNoticia.begin();
                                        tagsPos = itElementoNoticia->getListaPositivosReferencia();

                                        //me fijo en la lista de positivos de la noticia si estan todas las
                                        //categorias que me pidieron.					
										bInclude = false;
										countTemp = 0;
                               			for (iterTagsPos = tagsPos->begin();
                                            (iterTagsPos != tagsPos->end() && !bInclude);
                                             iterTagsPos++)
										{
											if (categoriasAIncluir.count(*iterTagsPos) > 0)
											{
												countTemp++;
											}
						
											bInclude = (countTemp == categoriasAIncluir.size());
										}

                                        //siempre se entra al for anterior ya que el front end, en caso
										//de no incluir una categoria por si, y solo por no, automatica
										//mente incluye todas las categorias en la consulta en el conjunto
										//por si
                                        if (bInclude)
                                        {
                                                //para todas las categorias que me pidieron que no esten...
                                                //me fijo en los POSITIVOS de la noticia a ver si la tiene.
                                                tagsNeg = itElementoNoticia->getListaPositivosReferencia();

                                                //si llegue al final, quiere decir que encontro
                                                //todas, entonces me fijo si no esta excluida por
                                                //la lista de categorias a no incluir
                                                iterTagsNeg = tagsNeg->begin();
                                                bExclude = false;
                                                
                                                for (itParaNegativo = categoriasANoIncluir.begin();
                                                     (itParaNegativo != categoriasANoIncluir.end() && (!bExclude));
                                                     itParaNegativo++)
                                                {
                                                        for ( iterTagsNeg = tagsNeg->begin() ;
                                                              (iterTagsNeg != tagsNeg->end() && (!bExclude)) ;
                                                              iterTagsNeg++ )
                                                        {
                                                                bExclude = bExclude || (*iterTagsNeg == *itParaNegativo);
                                                        }
                                                }
                                                
                                                if (!bExclude)
                                                {
                                                        //llegue al final del itParaNegativo
                                                        //entonces la noticia me interesa pq no encontre match.
                                                        resultadoInclusion.insert(*iteradorNoticias);
                                                }
                                        }
                                }
                        }
                }
        }

        return resultadoInclusion;

        
        /*
        ConjuntoTagItemConsulta resultadoInclusion;
        ConjuntoTagItemConsulta itemsCategoria;
        ConjuntoTagItemConsulta itemsInterseccion;
        ConjuntoTagItemConsulta itemsDiferencia;

        insert_iterator<ConjuntoTagItemConsulta> iteradorResultInterseccion(itemsInterseccion,
                itemsInterseccion.begin());
        insert_iterator<ConjuntoTagItemConsulta> iteradorResultDiferencia(itemsDiferencia,
                itemsDiferencia.begin());

        ConjuntoTagItemConsulta::iterator iteradorItemsInterseccion;
        ConjuntoTagItemConsulta::iterator iteradorItemsDiferencia;

        ConjuntoIdCategoria::iterator iteradorCategoriaAIncluir;
        ConjuntoIdCategoria::iterator iteradorCategoriaANoIncluir;

        for(iteradorCategoriaAIncluir = categoriasAIncluir.begin();
            iteradorCategoriaAIncluir != categoriasAIncluir.end();
            iteradorCategoriaAIncluir++)
        {
                itemsCategoria.clear();
                itemsCategoria = this->ConsultaPorCategoria(*iteradorCategoriaAIncluir);

                if (itemsCategoria.size() > 0)
                {
                        if (resultadoInclusion.size() > 0)
                        {
                        		itemsInterseccion.clear();
                        		
                                //ya habia algo, asi que intersecto
                                set_intersection(resultadoInclusion.begin(),
                                        resultadoInclusion.end(),
                                        itemsCategoria.begin(),
                                        itemsCategoria.end(),
                                        iteradorResultInterseccion);
                                //en itemsInterseccion me quedo la interseccion, asi que la tomo
                                //como resultado

                                resultadoInclusion.clear();

                                for(iteradorItemsInterseccion = itemsInterseccion.begin();
                                    iteradorItemsInterseccion != itemsInterseccion.end();
                                    iteradorItemsInterseccion++)
                                {
                                        resultadoInclusion.insert(*iteradorItemsInterseccion);
                                }
                        }
                        else
                        {
                                //no habia nada, copio todo...
                                resultadoInclusion = itemsCategoria;
                        }
                }
        }

        

        for (iteradorCategoriaANoIncluir = categoriasANoIncluir.begin();
             iteradorCategoriaANoIncluir != categoriasANoIncluir.end();
             iteradorCategoriaANoIncluir++)
        {
                itemsCategoria.clear();
                itemsCategoria = this->ConsultaPorCategoria(*iteradorCategoriaANoIncluir);

                if (itemsCategoria.size() > 0)
                {
                        if (resultadoInclusion.size() > 0)
                        {
                        		itemsDiferencia.clear();
                        		
                                //habia algo y tengo que restarle algo...
                                //itemsDiferencia
                                set_difference(resultadoInclusion.begin(),
                                        resultadoInclusion.end(),
                                        itemsCategoria.begin(),
                                        itemsCategoria.end(),
                                        iteradorResultDiferencia);

                                resultadoInclusion.clear();

                                for (iteradorItemsDiferencia = itemsDiferencia.begin();
                                        iteradorItemsDiferencia != itemsDiferencia.end();
                                        iteradorItemsDiferencia++)
                                {
                                        resultadoInclusion.insert(*iteradorItemsDiferencia);
                                }
                        }
                }
        }

        return resultadoInclusion;
        */
}

bool TagsManager::SacarNoticia(TCId idCategoria, TCId idFeed,
        TCPosicionEnLista posicion)
{
        RegistroFijoTagIndex registroData;

        Archivo archivoCategoria;
        archivoCategoria.setNombreArchivo(this->directorioBase +
                IdToString(idCategoria) + ".idx");

        bool bFound = false;

        if (archivoCategoria.abrirArchivo(maActualizacion))
        {
                archivoCategoria.posicionarEnArchivo(0);
                int posicionActual = 0;
                
                while (!archivoCategoria.getEsFinDeArchivo() && (!bFound))
                {
                        if (archivoCategoria.leerRegistro((Registro*)&registroData))
                        {
                                if (registroData.getEstado())
                                {
                                        if ((registroData.getIdFeed() == idFeed) &&
                                            (registroData.getPosicionEnIndice() == posicion))
                                        {
                                                if (archivoCategoria.posicionarEnArchivo(posicionActual * registroData.getTamanioRegistro()))
                                                {
                                                        registroData.setEstado(false);
                                                        archivoCategoria.escribirRegistro((Registro*)&registroData);
                                                        
                                                        bFound = true;
                                                }
                                        }
                                }
                        }

                        posicionActual++;
                }
                
                archivoCategoria.cerrarArchivo();
        }        

        return bFound;
}

void TagsManager::ReorganizarArchivos(ConjuntoIdCategoria &categoriasALimpiar, NewsManager &newsManager)
{
        ConjuntoIdCategoria::iterator iteradorCategoria;
        RegistroFijoTagIndex registroData;

        Archivo archivoCategoriaAnterior;
        Archivo archivoCategoriaReorganizado;

        bool archivoCreado;
        string sNombreArchivo;
        string sNombreArchivoTemp;

        for (iteradorCategoria = categoriasALimpiar.begin();
             iteradorCategoria != categoriasALimpiar.end();
             iteradorCategoria++)
        {
                archivoCreado = false;

                sNombreArchivo = this->directorioBase + IdToString(*iteradorCategoria) + ".idx";
                archivoCategoriaAnterior.setNombreArchivo(sNombreArchivo);

                sNombreArchivoTemp = this->directorioBase + IdToString(*iteradorCategoria) + ".idx.temp";
                archivoCategoriaReorganizado.setNombreArchivo(sNombreArchivoTemp);

                archivoCreado = archivoCategoriaReorganizado.crearArchivo();

                if (archivoCategoriaAnterior.abrirArchivo(maLectura) &&
                    archivoCreado)
                {
                        archivoCategoriaAnterior.posicionarEnArchivo(0);
                        archivoCategoriaReorganizado.posicionarAlFinal();

                        while (!archivoCategoriaAnterior.getEsFinDeArchivo())
                        {
                                if (archivoCategoriaAnterior.leerRegistro((Registro*)&registroData))
                                {
                                        if (registroData.getEstado())
                                        {
                                                //si no esta dado de baja logica...
                                                if (newsManager.getEstadoNoticia(registroData.getPosicionEnIndice(), registroData.getIdFeed()))
                                                {
                                                        //si la noticia sigue existiendo en el feed.
                                                        archivoCategoriaReorganizado.escribirRegistro((Registro*)&registroData);
                                                }
                                        }
                                }
                        }

                        archivoCategoriaAnterior.cerrarArchivo();
                        archivoCategoriaReorganizado.cerrarArchivo();

                        Archivo::EliminarArchivo(sNombreArchivo.c_str());
                        Archivo::RenombrarArchivo(sNombreArchivoTemp.c_str(), sNombreArchivo.c_str());
                }
        }
}

/* Implementacion Tag Item Consulta*/

TagItemConsulta::TagItemConsulta()
{
}

TagItemConsulta::~TagItemConsulta()
{
}

TCId TagItemConsulta::getIdFeed() const
{
        return this->idFeed;
}

TCPosicionEnLista TagItemConsulta::getPosicion() const
{
        return this->posicion;
}

TCTimeStamp TagItemConsulta::getFechaNoticia() const
{
        return this->fechaNoticia;
}

void TagItemConsulta::setIdFeed(TCId idFeed)
{
        this->idFeed = idFeed;
}

void TagItemConsulta::setPosicion(TCPosicionEnLista posicion)
{
        this->posicion = posicion;
}

void TagItemConsulta::setFechaNoticia(TCTimeStamp fechaNoticia)
{
        this->fechaNoticia = fechaNoticia;
}

bool TagItemConsulta::less(const TagItemConsulta &otroTagItemConsulta) const
{
        return
        (this->getFechaNoticia() < otroTagItemConsulta.getFechaNoticia())
        ||
        ((this->getIdFeed() < otroTagItemConsulta.getIdFeed())
        ||
        (this->getPosicion() < otroTagItemConsulta.getPosicion()));

}

//FUNCION GLOBAL DE COMPARACION DE TAGITEMCONSULTA
bool operator < (const TagItemConsulta & ltagitemconsulta,
        const TagItemConsulta & rtagitemconsulta)
{
         return ltagitemconsulta.less(rtagitemconsulta);
}




